[待望のアプデ]EC2インスタンスメタデータサービスv2がリリースされてSSRF脆弱性等への攻撃に対するセキュリティが強化されました!
こんにちは、臼田です。
皆さんセキュリティ対策してますか?(挨拶
今回はEC2インスタンスメタデータサービスv2がリリースされたのでこの機能について解説していきます。
EC2インスタンスメタデータとそのセキュリティについて
まず背景を説明します。EC2のインスタンスメタデータはOS上から取得できるEC2のメタデータ情報で、例えばインスタンスIDとかインスタンスタイプ、所属しているAZなどが取得できます。例えばOS上で動くスクリプトのデータにインスタンスIDを入れることによって、ログを見やすくしたりできます。
メタデータの取得方法はインスタンスメタデータサービス(IMDS)が169.254.169.254
というリンクローカルIPで動作していて、curlコマンド等によりアクセスして取得していました。具体的には下記のようなコマンドです。
[ec2-user ~]$ curl http://169.254.169.254/latest/meta-data/
しかし、昨今このメタデータが悪用されるケースが増えてきました。
メタデータの中にはインスタンスプロファイルというEC2に割り当てられた権限が利用できる一時的なアクセスキーを発行する機能があり、通常EC2はこれを利用して他のAWSサービスと連携します。例えば、S3へのアクセスを許可したIAM RoleをEC2に割り当てると、EC2内のスクリプト等からS3にアクセスすることができるようになります。これは、スクリプト内にIAM Userのアクセスキーを埋め込まなくて良くなるためセキュアに設計することが出来ます。
インスタンスプロファイルを利用したアクセス自体はIAM Userのアクセスキーを利用するよりはセキュアですが、このインスタンスプロファイルを外部から取得することが可能な脆弱性と組み合わせられる事によりクレデンシャルが漏洩し、そのEC2に割り当てられた権限を悪用することが可能になります。
有名な事故事例としては米金融大手 Capital Oneの1億人を超える個人情報流出があります。
詳細はSSRF攻撃によるCapital Oneの個人情報流出についてまとめてみた - piyologに記載がありますが、下記のようにSSRFの脆弱性があるWAFの設定を利用してEC2インスタンスのメタデータからクレデンシャルを取得し、その権限でアクセスできるS3上の個人情報を取得したとあります。(図は該当記事から引用)
このようにSSRF等の脆弱性と組み合わせられる事により影響の大きい問題になることから、EC2にアタッチするIAM Roleの管理や、SSRFのような脆弱性を生み出さないためのアプリケーションでの対策などに留意する必要がありました。
EC2インスタンスメタデータサービスv2でできること
EC2インスタンスメタデータサービスv2(IMDSv2)での改善点を簡単にまとめると下記のようになります。
- v2へのアクセスには事前に取得したTokenを必須とする
- TokenはPUTで取得する必要がある
- Tokenリクエスト時に有効期限(秒)を設定できる
- Tokenはヘッダに入れてリクエストする必要がある
- v1を無効化できる(デフォルトでは併用可能)
- メタデータサービス自体を無効化できる
- EC2インスタンスを立ち上げる際にv2のみに設定することをIAMで強制することができる
- X-Forwarded-ForヘッダーがあるリクエストにTokenを発行しない
- メタデータレスポンスのTTLを短くし複数ホストを経由した取得を防止できる
これによって以下のような環境での多層防御の役割を果たすと言われています。
- オープンなWebサイトアプリケーションファイアウォールからの保護
- 殆どのWAFがサポートしないPUTリクエストを採用したため
- オープンリバースプロキシから保護する
- Apache httpdやSquid等のプロキシで通常付与する
X-Forwarded-For
ヘッダーがある場合にTokenを発行しないため
- Apache httpdやSquid等のプロキシで通常付与する
- SSRFの脆弱性から保護する
- PUTでセッショントークンを取得して別のリクエストでトークンを要求する事により攻撃難易度をあげているため
- オープンレイヤー3ファイアウォールおよびNATからの保護
- TTLを1にすることによりインスタンスより外にパケットを出さないため
非常に強力なセキュリティ対策として利用できると思います!
EC2インスタンスメタデータサービスv2の設定やってみた
それでは実際にIMDSv2の設定をやってみます。
現状(2019/11/20)ではAWSマネジメントコンソール上では設定が出来ないようなのでAWSCLIでやっていきます。
まず、AWSCLIを最新版にアップデートします。
$ sudo pip install -U awscli $ aws --version aws-cli/1.16.286 Python/3.6.1 Darwin/17.7.0 botocore/1.13.22
次にインスタンスを作成します。そして設定を確認するとメタデータのオプションが追加されていました。
$ aws ec2 describe-instances --instance-id i-9999999999999999 --query "Reservations[*].Instances[*].MetadataOptions" [ [ { "State": "applied", "HttpTokens": "optional", "HttpPutResponseHopLimit": 1, "HttpEndpoint": "enabled" } ] ]
各パラメータは次のような役割です。
- HttpTokens: メタデータ利用にTokenが必要かどうか。デフォルトoptional。requiredにすると必須になる(v2のみになる)
- HttpPutResponseHopLimit: Token取得時のPutのレスポンスで付与するホップ数。デフォルト1
- HttpEndpoint: メタデータサービスが有効かどうか。デフォルトenabled
詳細はこちら: ModifyInstanceMetadataOptions-Amazon Elastic Compute Cloud
ちなみに既存のインスタンスでもメタデータのオプションは同様の値でした。
とりあえず普通に起動した状態でEC2上からメタデータにアクセスしてみます。まずはv1でのアクセス。
$ curl http://169.254.169.254/latest/meta-data/ ami-id ami-launch-index ami-manifest-path block-device-mapping/ …以下省略…
普通にアクセスできました。
続いてv2でのアクセス。
$ TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` \ > && curl -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/meta-data/ % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 56 100 56 0 0 9333 0 --:--:-- --:--:-- --:--:-- 11200 * Trying 169.254.169.254... * TCP_NODELAY set * Connected to 169.254.169.254 (169.254.169.254) port 80 (#0) > GET /latest/meta-data/ HTTP/1.1 > Host: 169.254.169.254 > User-Agent: curl/7.61.1 > Accept: */* > X-aws-ec2-metadata-token: AQAA******************************************************== > * HTTP 1.0, assume close after body < HTTP/1.0 200 OK < Accept-Ranges: bytes < Content-Length: 298 < Content-Type: text/plain < Date: Wed, 20 Nov 2019 08:30:23 GMT < Last-Modified: Wed, 20 Nov 2019 07:58:36 GMT < X-Aws-Ec2-Metadata-Token-Ttl-Seconds: 21600 < Connection: close < Server: EC2ws < ami-id ami-launch-index ami-manifest-path block-device-mapping/ …以下省略…
PUTしてTokenを取得し、それをX-aws-ec2-metadata-token
ヘッダーに入れてリクエストします。
続いてv1を無効化してみましょう。手元に戻ってきて下記を実行します。
$ aws ec2 modify-instance-metadata-options --instance-id i-9999999999999999 --http-token required --http-endpoint enabled { "InstanceId": "i-9999999999999999", "InstanceMetadataOptions": { "State": "pending", "HttpTokens": "required", "HttpPutResponseHopLimit": 1, "HttpEndpoint": "enabled" } }
参考例では--http-endpoint enabled
がありませんでしたが、今回はエラーになるためつけました。それではもう一度EC2上からv1を利用してみます。
$ curl http://169.254.169.254/latest/meta-data/ <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>401 - Unauthorized</title> </head> <body> <h1>401 - Unauthorized</h1> </body> </html>
401エラーとなりました。これでv1を無効化できていることが確認できました。
IMDSに関するエラーは下記のようになっています。
- 400 Missing or Invalid Parameters: PUT要求は無効です。
- 401 Unauthorized: GET要求で無効なトークンが使用されています。推奨されるアクションは、新しいトークンを生成することです。
- 403 Forbidden: 要求が許可されていないか、インスタンスメタデータサービスがオフになっています。
続いてIMDSエンドポイントを無効化してみます。
$ aws ec2 modify-instance-metadata-options --instance-id i-9999999999999999 --http-endpoint disabled { "InstanceId": "i-9999999999999999", "InstanceMetadataOptions": { "State": "pending", "HttpTokens": "required", "HttpPutResponseHopLimit": 1, "HttpEndpoint": "disabled" }
v1とv2それぞれリクエストしてみます。
# v1 $ curl http://169.254.169.254/latest/meta-data/ <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>403 - Forbidden</title> </head> <body> <h1>403 - Forbidden</h1> </body> </html> # v2 $ curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600" <?xml version="1.0" encoding="iso-8859-1"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> <head> <title>403 - Forbidden</title> </head> <body> <h1>403 - Forbidden</h1> </body> </html>
どちらも403となりました。使えなくなったことが確認できました。
まとめ
EC2のインスタンスメタデータv2についてどのように改善されたか解説しました。
これまではアプリケーションレイヤーやIAM Roleの設計・ポリシーでの対策が必要だったところを、メタデータサービス側で対策を強化することができるようになったので、環境問わずセキュリティを向上することが見込めるアップデートです。
是非活用してみてください。
また、今回は詳細に解説しませんでしたが、実際のSSRFの脆弱性への有効性や、IAMへのv2強制なども試してみたいと思います。
(2019/12/10追記): 徳丸先生がSSRF対策としての検証と考察をブログに乗せていらっしゃいます。必読!
徳丸浩の日記: SSRF対策としてAmazonから発表されたIMDSv2の効果と限界
既存環境でのv2への移行方法もこちらにありますので参考にしてみてください。